package com.gitee.cliveyuan.tools.data;

import com.gitee.cliveyuan.tools.Assert;
import com.gitee.cliveyuan.tools.CollectionTools;
import com.gitee.cliveyuan.tools.StringTools;
import com.gitee.cliveyuan.tools.enums.ExcelType;
import com.gitee.cliveyuan.tools.exception.ExcelException;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by Clive on 2019/10/28.
 */
@Slf4j
public class TableUtils {

    public static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static <T> List<T> dataToObject(int skipRowNo, List<String[]> data, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        try {
            if (data == null || data.isEmpty()) return new ArrayList<>();
            Field[] fields = clazz.getDeclaredFields();
            if (skipRowNo > 0 && skipRowNo <= data.size()) {
                data = data.subList(skipRowNo, data.size());
            }
            for (String[] dataArray : data) {
                T t = clazz.newInstance();
                for (int i = 0; i < fields.length; i++) {
                    Field field = fields[i];
                    PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                    Method getMethod = pd.getWriteMethod();
                    if (getMethod != null) {
                        String strVal = StringTools.EMPTY;
                        if (i < dataArray.length) {
                            strVal = dataArray[i];
                        }
                        if (StringTools.isBlank(strVal)) continue;
                        Object value = strVal.trim();
                        Class<?>[] parameterTypes = getMethod.getParameterTypes();
                        Class<?> parameterType = parameterTypes[0];
                        if (Boolean.class.equals(parameterType) || boolean.class.equals(parameterType)) {
                            value = Boolean.valueOf(strVal.trim());
                        } else if (Integer.class.equals(parameterType) || int.class.equals(parameterType)) {
                            value = Integer.valueOf(strVal.trim());
                        } else if (Long.class.equals(parameterType) || long.class.equals(parameterType)) {
                            value = Long.valueOf(strVal.trim());
                        } else if (Double.class.equals(parameterType) || double.class.equals(parameterType)) {
                            value = Double.valueOf(strVal.trim());
                        } else if (Float.class.equals(parameterType) || float.class.equals(parameterType)) {
                            value = Float.valueOf(strVal.trim());
                        } else if (BigDecimal.class.equals(parameterType)) {
                            value = new BigDecimal(strVal.trim());
                        } else if (Date.class.equals(parameterType)) {
                            value = SDF.parse(strVal.trim());
                        } else if (String.class.equals(parameterType)) {
                            value = strVal.trim();
                        }
                        getMethod.invoke(t, value);
                    }
                }
                list.add(t);
            }
        } catch (Exception e) {
            log.error("dataToObject Exception", e);
        }
        return list;
    }

    public static File generate(ExcelGenerateParam param)  {
        String filePath = param.getFilePath();
        ExcelType excelType = param.getExcelType();
        Assert.notBlank(filePath, "file path can't be empty");
        Assert.notNull(excelType, "excel type is required");
        Assert.notEmpty(param.getSheetContentList(), "sheetContentList can't be empty");

        File file = new File(filePath);
        if (!file.exists()) {
            boolean result = file.mkdirs();
            if (!result) throw ExcelException.failToMkdirs();
        }
        String fileName = System.currentTimeMillis() + "." + excelType.name().toLowerCase();
        if (!filePath.endsWith("/")) filePath = filePath + "/";
        file = new File(filePath + fileName);
        try (FileOutputStream fos = new FileOutputStream(file)) {
            Workbook wb;
            if (excelType.equals(ExcelType.XLS)) wb = new HSSFWorkbook();
            else wb = new XSSFWorkbook();

            for (SheetContent sheetContent : param.getSheetContentList()) {
                String[] headers = sheetContent.getHeaders();
                Collection data = sheetContent.getData();
                Assert.isTrue(CollectionTools.isNotEmpty(headers), "headers can't be empty");
                Assert.isTrue(CollectionTools.isNotEmpty(data), "data can't be empty");

                Sheet sheet = wb.createSheet(sheetContent.getSheetName());
                Row firstRow = sheet.createRow(0);
                //创建表头
                for (int i = 0; i < headers.length; i++) {
                    Cell cell = firstRow.createCell(i);
                    cell.setCellValue(headers[i]);
                }

                //处理数据
                Iterator it = data.iterator();
                int index = 0;
                while (it.hasNext()) {
                    index++;
                    Row row = sheet.createRow(index);
                    Object t = it.next();
                    //反射
                    Class<?> clazz = t.getClass();
                    Field[] fields = clazz.getDeclaredFields();
                    for (int i = 0; i < fields.length; i++) {
                        Field field = fields[i];
                        PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                        Method getMethod = pd.getReadMethod();
                        if (getMethod != null) {
                            Object invoke = getMethod.invoke(t);
                            String value = StringTools.EMPTY;
                            if (invoke != null) {
                                if (invoke instanceof Date) {
                                    value = TableUtils.SDF.format((Date) invoke);
                                } else value = invoke.toString();
                            }
                            Cell cell = row.createCell(i);
                            cell.setCellValue(value);
                        }

                    }
                }
            }
            wb.write(fos);
            return file;
        } catch (Exception e) {
            log.error("excel generate exception", e);
            throw ExcelException.failToGenerateExcel();
        }

    }
}
